import fs from 'fs';
import util from 'util'
import { ipcMain, Event, dialog, BrowserWindow, FileFilter, OpenDialogOptions } from 'electron';
import IElectronModule from './bg_base';
import { IpcConnnectionBase, IpcReturnValue, IpcReturnStatus, IpcSendTypeNode } from './../plugin/ipc_renderer_ex';
import { Buffer } from 'buffer';

/** 打开文件对话框 */
export const FILE_OPEN_DIALOG = "file-open-dialog";
const FILE_OPEN_DIALOG_RET = "file-open-dialog-ret";
/** 打开文件，并返回文件操作句柄 */
export const FILE_OPEN = "file-open";
const FILE_OPEN_RET = "file-open-ret";
/** 通过文件句柄，读取文件 */
export const FILE_READ = "file-read";
const FILE_READ_RET = "file-read-ret";
/** 通过文件句柄，写入文件 */
export const FILE_WRITE = "file-write";
const FILE_WRITE_RET = "file-write-ret";
/** 通过文件句柄，关闭相应的文件流 */
export const FILE_CLOSE = "file-close";
const FILE_CLOSE_RET = "file-close-ret";


// 定义ipc接口
declare module '@/plugin/ipc_renderer_ex' {
    interface IpcSendTypes {
        [FILE_OPEN_DIALOG]: IpcSendTypeNode<IpcOpenDialogOption, IpcReturnValue<string[]>>
        [FILE_OPEN]: IpcSendTypeNode<IpcFileOpenOption, IpcReturnValue<number>>
        [FILE_READ]: IpcSendTypeNode<IpcFileReadOption, IpcReturnValue<{ bytesRead: number, buffer: Buffer }>>
        [FILE_CLOSE]: IpcSendTypeNode<IpcFileHandleOption, IpcReturnValue<boolean>>
    }
}

/** 打开文件对话框配置接口 */
export interface IpcOpenDialogOption /*extends OpenDialogOptions*/ {
    /** 标题 */
    title?: string
    /** 默认路径 */
    defaultPath?: string
    /** “确认”按钮文字 */
    buttonLabel?: string
    /** 文件过滤 */
    filters?: FileFilter[]
    /** 文件操作类型 */
    properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory'>
}


export interface IpcFileOpenOption {
    /** 文件路径 */
    path: fs.PathLike
    /**
     * 文件系统标志
     * - 格式： (a[xs]?|rs?|wx?)+?
     *   - a : 打开并追加
     *   - r : 打开以读取
     *   - w : 打开以写入
     *   - x : 文件已存在则失败
     *   - s : 以同步模式打开
     *   - \+ : 同时获取 读/写 的另一个权限
     */
    flags: string | number
    /** 设置文件模式（权限和粘滞位） */
    mode?: string | number | null
}

/** 文件句柄参数 */
export interface IpcFileHandleOption {
    /** 打开文件的句柄 */
    fd: number
}

/** 读取文件参数 */
export interface IpcFileReadOption extends IpcFileHandleOption {
    /** 读取长度 */
    length: number
    /** 读取坐标，null 表示从当前位置读取 */
    position: number | null
}

/** 文件操作的辅助工具 */
class FileHelper implements IElectronModule {

    private win?: BrowserWindow;
    //private _database_map: Map<string, any> = new Map<string, any>();

    install(win: Electron.BrowserWindow): void {
        this.win = win;
        ipcMain.on(FILE_OPEN_DIALOG, this.fileOpenDialog.bind(this));
        ipcMain.on(FILE_OPEN, this.fileOpen.bind(this));
        ipcMain.on(FILE_READ, this.fileRead.bind(this));
        ipcMain.on(FILE_CLOSE, this.fileClose.bind(this, FILE_CLOSE_RET));
    }

    /** 弹出“打开文件”对话框的异步操作 */
    private static showOpenDialogAsync(
        browserWindow: BrowserWindow,
        options: OpenDialogOptions
    ): Promise<{ filePaths: string[], bookmarks: string[] }> {
        return new Promise((resolve, reject) => {
            dialog.showOpenDialog(
                browserWindow,
                options,
                (filePaths: string[], bookmarks: string[]) => {
                    if (filePaths) {
                        resolve({ filePaths, bookmarks });
                    } else {
                        reject({
                            status: IpcReturnStatus.fail,
                            message: "未选择文件"
                        });
                    }
                }
            )
        });
    }
    /** 打开文件的异步操作 */
    private static fs_openAsync = util.promisify(fs.open);
    private static fs_readAsync = util.promisify(fs.read);
    private static fs_closeAsync = util.promisify(fs.close);


    private async fileOpenDialog(
        event: Event,
        options: OpenDialogOptions
    ): Promise<void> {
        if (this.win) {
            try {
                let { filePaths, bookmarks } = await FileHelper.showOpenDialogAsync(this.win, options);
                event.sender.send(FILE_OPEN_DIALOG_RET, {
                    status: IpcReturnStatus.success,
                    message: "选择了文件",
                    value: filePaths
                } as IpcReturnValue<string[]>);
            } catch ({ status, message }) {
                event.sender.send(FILE_OPEN_DIALOG_RET, {
                    status: status,
                    message: message,
                    value: []
                } as IpcReturnValue<string[]>);
            }
        } else {
            event.sender.send(FILE_OPEN_DIALOG_RET, {
                status: IpcReturnStatus.fail,
                message: "窗口未关联",
                value: []
            } as IpcReturnValue<string[]>);
        }
    }

    /** 打开文件，并返回文件操作句柄 */
    private async fileOpen(
        event: Event,
        { path, flags, mode }: IpcFileOpenOption
    ) {
        try {
            //let { path, flags, mode } = options;
            let fd = await FileHelper.fs_openAsync(path, flags, mode);
            event.sender.send(FILE_OPEN_RET, {
                status: IpcReturnStatus.success,
                message: "成功打开文件",
                value: fd
            } as IpcReturnValue<number>);
        } catch (error) {
            let { message, stack, status } = error;
            event.sender.send(FILE_OPEN_RET, {
                status: status || IpcReturnStatus.error,
                message: message,
                value: -1
            } as IpcReturnValue<number>);
        }
    }

    private async fileRead(
        event: Event,
        { fd, length, position }: IpcFileReadOption
    ) {
        try {
            let read_buffer = Buffer.alloc(length);
            let { bytesRead, buffer } = await FileHelper.fs_readAsync(fd, read_buffer, 0, length, position);
            event.sender.send(FILE_READ_RET, {
                status: IpcReturnStatus.success,
                message: `成功读取长度 ${bytesRead}。`,
                value: { bytesRead, buffer }
            } as IpcReturnValue<{ bytesRead: number, buffer: Buffer }>);
        }
        catch (error) {
            let { message, stack, status } = error;
            event.sender.send(FILE_READ_RET, {
                status: status || IpcReturnStatus.error,
                message: message,
                value: undefined
            } as IpcReturnValue<Buffer>);
        }
    }

    private async fileClose(
        ret_tag: string,
        event: Event,
        { fd }: IpcFileHandleOption
    ) {
        try {
            await FileHelper.fs_closeAsync(fd);
            event.sender.send(ret_tag, {
                status: IpcReturnStatus.success,
                message: `成功关闭文件：${fd}。`,
                value: true
            } as IpcReturnValue<boolean>);
        }
        catch (error) {
            let { message, stack, status } = error;
            event.sender.send(ret_tag, {
                status: status || IpcReturnStatus.error,
                message: message,
                value: false
            } as IpcReturnValue<boolean>);
        }

    }
}

export default new FileHelper();